#include "HookLibrary.h"
#include "FileWatcher.h"
#include "EventHandler.h"
#include "KeyboardProc.h"
#include "MessageWindow.h"

LPDEADKEY_INFO WINAPI CreateDeadKeyInfo(UINT, UINT, LPBYTE);
VOID           WINAPI ClearDeadKeyState(INT);
DWORD          WINAPI GetCharFromKeyDown(UINT, UINT, LPBYTE, LPWSTR, DWORD);

__declspec(dllexport) LRESULT CALLBACK KeyboardProc(INT32 nCode, WPARAM wParam, LPARAM lParam)
{
	HWND hWnd     = NULL;
	WORD lParamHi = HIWORD(lParam);

	EnterCriticalSection(&g_csModule);

	if (nCode != HC_ACTION)
	{
		goto CallNextHook;
	}

	hWnd = MsgWindow();

	if (hWnd == NULL)
	{
		goto CallNextHook;
	}

	EnsureFileWatcher(hWnd);

	if (!(lParamHi & KF_UP))
	{
		UINT wVirtualKey = wParam;
		UINT wScanCode   = (lParam & 0xff0000) >> 16;
		BYTE KeyState[256];

		if (GetKeyboardState(KeyState))
		{
			DWORD  cch        = 0;
			SIZE_T cchResult  = 128;
			LPWSTR lpszResult = BGHeapAllocStr(cchResult);

			DWORD  Msg = GetWinFormsKey(wVirtualKey,
				                        KeyState[VK_CONTROL] & 0x80,
										KeyState[VK_SHIFT] & 0x80,
										lParamHi & KF_ALTDOWN);

			if (IsKeyDownEventRegistered(Msg))
			{
				PostKeyDownEvent(hWnd, Msg);
			}

			cch = GetCharFromKeyDown(wVirtualKey, wScanCode, KeyState, lpszResult, cchResult);

			if (cch > 0)
			{

			}

			BGHeapFree(lpszResult);
		}
	}

CallNextHook:

	LeaveCriticalSection(&g_csModule);
	return CallNextHookEx(NULL, nCode, wParam, lParam);
}

DWORD WINAPI GetCharFromKeyDown(UINT wVirtualKey, UINT wScanCode, LPBYTE lpKeyState, LPWSTR lpszResult, DWORD cchResult)
{
	INT cch = ToUnicode(wVirtualKey, wScanCode, lpKeyState, lpszResult, cchResult, 0);
	LPDEADKEY_INFO lpDeadKey;

	if (cch > 0)
	{
		lpDeadKey = GetLastDeadKey();

		if (lpDeadKey != NULL)
		{
			WCHAR lpBuffer[128];

			ToUnicode(lpDeadKey->wVirtualKey,
				      lpDeadKey->wScanCode,
					  lpDeadKey->lpKeyState,
					  lpBuffer, 128, 0);
		}

		SetLastDeadKey(NULL);
	}
	else if (cch < 0)
	{
		lpDeadKey = CreateDeadKeyInfo(wVirtualKey, wScanCode, lpKeyState);
		SetLastDeadKey(lpDeadKey);

		ClearDeadKeyState(cch);
	}

	return cch;
}

LPDEADKEY_INFO WINAPI GetLastDeadKey()
{
	return TlsGetValue(g_dwTlsIndex);
}

VOID WINAPI SetLastDeadKey(LPDEADKEY_INFO lpInfo)
{
	LPDEADKEY_INFO lpOldInfo = GetLastDeadKey();

	if (lpOldInfo != NULL)
	{
		BGHeapFree(lpOldInfo);
	}

	TlsSetValue(g_dwTlsIndex, lpInfo);
}

LPDEADKEY_INFO WINAPI CreateDeadKeyInfo(UINT wVirtualKey, UINT wScanCode, LPBYTE lpKeyState)
{
	INT i;
	LPDEADKEY_INFO lpInfo = BGHeapAlloc(sizeof(DEADKEY_INFO));

	lpInfo->wVirtualKey = wVirtualKey;
	lpInfo->wScanCode = wScanCode;

	for (i = 0; i < sizeof(lpInfo->lpKeyState); ++i)
	{
		lpInfo->lpKeyState[i] = lpKeyState[i];
	}

	return lpInfo;
}

VOID WINAPI ClearDeadKeyState(INT cch)
{
	CONST INT wVirtualKey = VK_DECIMAL;
	CONST INT wScanCode   = MapVirtualKeyW(wVirtualKey, MAPVK_VK_TO_VSC);

	while (cch < 0)
	{
		BYTE  lpKeyStateEmpty[256];
		WCHAR lpBuffer[128];
		
		cch = ToUnicode(VK_DECIMAL, wScanCode, lpKeyStateEmpty, lpBuffer, 128, 0);
	}
}
